/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package tenorserver;

import DataModel.ChallengeData;
import TransportLayer.IServiceListener;
import TransportLayer.NotaryRequest;
import TransportLayer.NotaryRequestPacket;
import TransportLayer.NotaryResponse;
import TransportLayer.NotaryResponseType;
import cryptography.IVerificationService;
import cryptography.TenorRandomGenerator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Tomasz Nowak
 */
public class ServiceListener implements IServiceListener
{
	java.net.DatagramSocket socket;
	ObjectInputStream objectInputStream;
	ByteArrayOutputStream byteArrayOutputStream;
	int verificationChallengeSize;
	IVerificationService verificationService;

	public ServiceListener( ServiceListenerSettings serviceListenerSettings, IVerificationService verificationService ) throws SocketException
	{
		serviceListenerSettings.load();
		socket = new DatagramSocket(serviceListenerSettings.getListeningPort(),
																serviceListenerSettings.getListeningAddress());
		verificationChallengeSize = serviceListenerSettings.getVerificationChallengeSize();
		this.verificationService = verificationService;
	}

	@Override
	public NotaryRequestPacket receiveRequest() throws IOException, ClassNotFoundException
	{
		byte[] packetLenghtArray = new byte[2];

		DatagramPacket packet = new DatagramPacket(packetLenghtArray, packetLenghtArray.length);
		socket.receive(packet);

		int packetlength = ( int ) packet.getData()[1] + ( int ) packet.getData()[0] * 256;

		byte[] packetArray = new byte[packetlength];
		packet = new DatagramPacket(packetArray, packetArray.length);
		socket.receive(packet);

		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(packet.getData());

		objectInputStream = new ObjectInputStream(byteArrayInputStream);
		return new NotaryRequestPacket(( NotaryRequest ) objectInputStream.readObject(),
																	 packet.getAddress(), packet.getPort());
	}

	@Override
	public void sendResponse( NotaryResponse notaryResponse, InetAddress address, int port ) throws IOException, ClassNotFoundException
	{
		byteArrayOutputStream = new ByteArrayOutputStream();

		ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

		objectOutputStream.writeObject(notaryResponse);
		objectOutputStream.flush();

		byte[] buffer = byteArrayOutputStream.toByteArray();

		byte[] packetbuffer = new byte[buffer.length + 2];

		byte[] lengthArray = ByteBuffer.allocate(4).putInt(buffer.length).array();

		packetbuffer[0] = lengthArray[2];
		packetbuffer[0] = lengthArray[3];

		DatagramPacket packet = new DatagramPacket(packetbuffer, packetbuffer.length, address, port);
		socket.send(packet);
	}
//zrobic timeouty

	@Override
	public boolean verifyClient( NotaryRequestPacket receivedRequest )
	{
		ChallengeData challengeData = new ChallengeData();

		challengeData.setData(new byte[verificationChallengeSize]);
		TenorRandomGenerator generator = TenorRandomGenerator.getInstance();
		generator.getBytes(challengeData.getData());
		boolean result = false;

		NotaryResponse response = new NotaryResponse(NotaryResponseType.VERIFICATION_Challenge, challengeData);
		try
		{
			sendResponse(response, receivedRequest.getSourceAddress(), receivedRequest.getSourcePort());
			NotaryRequestPacket challengeResponse = receiveRequest();
			result = verifyResponse(response, challengeResponse);
		}
		catch ( IOException ex )
		{
			Logger.getLogger(ServiceListener.class.getName()).log(Level.SEVERE, null, ex);
			return false;
		}
		catch ( ClassNotFoundException ex )
		{
			Logger.getLogger(ServiceListener.class.getName()).log(Level.SEVERE, null, ex);
		}

		return result;
	}

	@Override
	public void setVerificationService( IVerificationService verificationService )
	{
		this.verificationService = verificationService;
	}

	@Override
	public IVerificationService getvVerificationService()
	{
		return verificationService;
	}

	private boolean verifyResponse( NotaryResponse challenge, NotaryRequestPacket challengeResponse )
	{
		return verificationService.Verify(( ChallengeData ) challenge.getAdditionalData(),
																			( ChallengeData ) challengeResponse.getNotaryRequest().getAdditionalObject());
	}
}
